﻿using System;
using System.IO;
using System.Linq;
using Core;
using Core.Domain.Bases;
using Core.Domain.Entities;
using Core.Interfaces;
using Core.IocHelpers;
using Core.Settings;
using NHibernate;

namespace DataMain.Helpers
{
  [AutoRegister]
  public class EnvironmentDataHelper : IEnvironmentDataHelper
  {
    private readonly IAppSettings _appSettings;
    private readonly IEnvironment _environment;
    private ISession _session;

    public EnvironmentDataHelper(IEnvironment environment, IAppSettings appSettings)
    {
      _environment = environment;
      _appSettings = appSettings;
      _session = IoC.Container.Resolve<ISessionProvider>().GetSession();

      SetupFileWatcher();
    }

    #region IEnvironmentDataHelper Members

    public void RecreateSession()
    {
      _session = IoC.Container.Resolve<ISessionProvider>().GetSession(true);
    }

    public void AttachToCurrentData()
    {
      var community = LoadCommunity(_session);

      LoadElection(_session, community);

      var location = LoadVotingLocation(_session);

      LoadBallotEntryComputer(_session, location);

      LoadTeller(_session);
    }

    public event EventHandler<DataChangedEvent> DataChanged;

    #endregion

    private void SetupFileWatcher()
    {
      var watcher = new FileSystemWatcher(Path.GetDirectoryName(_appSettings.DbFilePath),
                                          Path.GetFileName(_appSettings.DbFilePath))
                      {
                            NotifyFilter = NotifyFilters.LastWrite,
                            EnableRaisingEvents = true
                      };
      watcher.Changed += ((sender, e) =>
                            {
                              if (DataChanged != null)
                              {
                                DataChanged.Invoke(sender, new DataChangedEvent(e));
                              }
                            });
    }

    private void LoadTeller(ISession session)
    {
      var tellerId = _appSettings.TellerId;
      var teller = (from t in session.CreateCriteria(typeof (Teller)).List<Teller>()
                    where t.Id == tellerId
                    select t).FirstOrDefault();
      if (teller == null)
      {
        teller = new Teller
                   {
                         FullName = "Developer",
                         ShortName = "Dev",
                         WorkingAtLocation = _environment.Location
                   };
        teller.Save();
        _appSettings.TellerId = teller.Id;
      }

      _environment.Teller = teller;
    }

    private void LoadBallotEntryComputer(ISession session, VotingLocation location)
    {
      var computerId = _appSettings.ComputerId;
      var computer = (from v in session.CreateCriteria(typeof (BallotEntryComputer)).List<BallotEntryComputer>()
                      where v.Id == computerId
                      select v).FirstOrDefault();
      if (computer == null)
      {
        var lastCode = (from v in session.CreateCriteria(typeof (BallotEntryComputer)).List<BallotEntryComputer>()
                        orderby v.Code descending
                        select v.Code).FirstOrDefault() ?? string.Empty;
        string newCode;
        if (lastCode.HasContent())
        {
          var lastChar = lastCode.Last();
          lastChar++;
          newCode = lastCode.Substring(0, lastCode.Length - 1) + lastChar;
        }
        else
        {
          newCode = "A";
        }
        computer = new BallotEntryComputer
                     {
                           Description = Environment.MachineName,
                           Code = newCode,
                           UsedAtLocation = location
                     };
        computer.Save();

        // remember this one
        _appSettings.ComputerId = computer.Id;
      }

      _environment.Computer = computer;
    }

    private VotingLocation LoadVotingLocation(ISession session)
    {
      var locationId = _appSettings.LocationId;
      var location = (from v in session.CreateCriteria(typeof (VotingLocation)).List<VotingLocation>()
                      where v.Id == locationId
                      select v).FirstOrDefault();
      if (location == null)
      {
        location = new VotingLocation
                     {
                           Description = "Computer " + Environment.MachineName
                     };
        location.Save();
        _appSettings.LocationId = location.Id;
      }
      _environment.Location = location;
      return location;
    }

    private void LoadElection(ISession session, Community community)
    {
      var electionId = _appSettings.ElectionId;
      var election = (from e in session.CreateCriteria(typeof (Election)).List<Election>()
                      where e.InCommunity == _environment.Community
                            && (e.Id == electionId || electionId == 0)
                      select e).FirstOrDefault();
      if (election == null)
      {
        election = new Election
                     {
                           ElectionDate = DateTime.Today,
                           InCommunity = community
                     };
        election.Save();
        _appSettings.ElectionId = election.Id;
      }
      election.Voters.Count(); // force a load -- is this needed?
      _environment.Election = election;
    }

    private Community LoadCommunity(ISession session)
    {
      var communityId = _appSettings.CommunityId;
      // default to most recent
      var community = (from c in session.CreateCriteria(typeof (Community)).List<Community>()
                       where communityId == c.Id || communityId == 0
                       orderby c.Id descending
                       select c).FirstOrDefault();
      if (community == null)
      {
        community = new Community
                      {
                            Name = "New Community"
                      };
        community.Save();
        _appSettings.CommunityId = community.Id;
      }
      _environment.Community = community;
      return community;
    }
  }
}